package com.aloha.springframework.aop;

import com.aloha.springframework.aop.framework.AdvisorChainFactory;
import com.aloha.springframework.aop.framework.DefaultAdvisorChainFactory;
import org.aopalliance.aop.Advice;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author DaiZhiHeng
 * @description 提供获取代理对象，配置代理方法等功能
 * @date 2023/7/7 9:48
 */
public class AdvisedSupport {

    /**
     * 封装了代理类
     */
    private TargetSource targetSource;

    /**
     * 是否使用cglib代理
     */
    private boolean proxyTargetClass = false;

    /**
     * 能够匹配代理类的advisor
     */
    private final List<Advisor> advisors = new ArrayList<>();

    /**
     * 再细粒度的划分，能够找到每个方法的advice
     * { key：方法, value：该方法所有的advice }
     */
    private final transient Map<Method, List<Advice>> methodCache;

    /**
     * 获取适用于给定方法的advice集合的工厂
     */
    AdvisorChainFactory advisorChainFactory = new DefaultAdvisorChainFactory();

    public AdvisedSupport() {
        this.methodCache = new ConcurrentHashMap<>(16);
    }

    /**
     * 用来返回方法的拦截器链
     */
    public List<Advice> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) {
        List<Advice> cached = methodCache.get(method);
        if (cached != null) return cached;
        // 获取 method 的所有 advice
        cached = advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(this, method, targetClass);
        methodCache.put(method, cached);
        return cached;
    }

    public boolean isProxyTargetClass() {
        return proxyTargetClass;
    }

    public void setProxyTargetClass(boolean proxyTargetClass) {
        this.proxyTargetClass = proxyTargetClass;
    }

    public void addAdvisor(Advisor advisor) {
        advisors.add(advisor);
    }

    public void addAdvisor(List<Advisor> advisors) {
        advisors.forEach(this::addAdvisor);
    }

    public List<Advisor> getAdvisors() {
        return advisors;
    }

    public TargetSource getTargetSource() {
        return targetSource;
    }

    public void setTargetSource(TargetSource targetSource) {
        this.targetSource = targetSource;
    }
}
